今天來介紹如何在 Next 路由導航,有兩種方式:
Link
componentuseRouter
hook這兩個都是 Next 提供的功能,兩個基本上有著相同的功能,差別在於使用方式不一樣。Link
component 提供一個實際的 tag 供使用者點按,useRouter
則是可以利用程式去控制 router 的跳轉功能。
最後會講到 Next 的路由導航運作方式 -> 這個才是重點呀,看看 Next 在背後都幫我們做掉了哪些事。
<Link>
ComponentuseRouter
hookLink
Component<Link>
是 Next 提供的組件,它繼承了 HTML 的 <a>
tag,並且提供了軟導航(soft navigating)與預取(prefeching)的功能。
<Link>
組件可以使用在 server component 與 client component 上。
soft navigating 與 prefetching 最後一章會提到
使用方式
import Link from 'next/link'
export default function Page() {
const path = '/about'
return (
<>
// 1. 導航到 /dashboard
<Link href="/dashboard">Dashboard</Link>
// 2. 使用樣板字串符也是完全沒問題滴
<Link href={`${path}`}>About</Link>
</>
)
}
useRouter
hookuseRouter
可以讓我們使用程式的方式做路由導航,適合用在我們想主動轉換導航,或是我們不想渲染出 <a>
tag 的時候。
因為是 react hook,所以只能 useRouter
運行在 client component 上。
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/dashboard')}>
Dashboard
</button>
)
}
在開始講運作機制之前,先來了解一下 server 與 client 不同的導航策略。
next 會依據每個 route 自動把 code 做切分,當使用者訪問時可以只執行該頁所需要的 code。
client 端會利用 prefetch 與 cache 的方式加速訪問頁面的速度,並且只渲染有差異的部分。
OK~現在正式進入導航運作機制的部分
prefetching 就是在我們尚未訪問頁面以前,Next 就已經提前將頁面的資料給載入了。
有兩種方式讓 Next 做到 prefetching
<Link>
進入到視窗時useRouter
的 prefetch()
method因為 useRouter
的關係,底下先以 client component 演示。Link
component 也可以在 server component 運行。
// in client component
'use client'
export default Page() {
// 1. 使用 useRouter,prefetch '/dashboard'
const router = useRouter;
router.pretch('/dashboard');
return (
// 2. 當 Link 組件進入到頁面可視範圍時,prefetch '/dashboard'
<Link href='/dashboard' />
)
}
有人可能會好奇 prefetch 回來的資料是什麼?
這些資料是RSC Payload
裡面包含了 server component 的渲染資訊、傳給 client component 的 props 與 client component placeholder,詳細的資訊可以操考官方文章
prefetch 就是預先跟 server 要接下來會訪問到的頁面資料,但有些路由包含動態內容,提前取得頁面內容有可能會導致內容偏誤。
所以 Next 對於靜態與動態的 routes 有不一樣的 prefetch 策略。
這裡講的動態 routes 是指「顯示的內容」是動態的,並不是指「訪問的路徑」是動態的。
關於如何判斷路由是否為動態可以參考 Day9 - Server Component - Next13 的渲染策略一文
layout.tsx
的 UI 還有第一個 loading.tsx
的 UI,而且只會幫你保留 30 秒我們也可以取消 prefetch 這個功能,只要在 <Link>
組件上傳入 prefetch: false
。
export default Page() {
return (
// 不自動 prefetch 頁面
<Link
href='/dashboard'
prefetch={false}
/>
)
}
Next 在 client 端有一個特殊的 cache 機制,可以把我們每個訪問過或 prefetch 過的頁面資料存起來。
當我們再次訪問時就可以渲染 cache 的結果,無需再跟 server 發送請求。
這跟 Day9 - Server Component 那章提到的 server-level cache 不一樣。路由的 cache 是存放在 client 端暫存記憶體當中。
底下是使用 router cache 的示意圖:
因為這些 cache 是存放在暫存記憶體當中,所以在我們按下重新整理或是關閉頁面後開起來,這份 cache 就沒了。
此外,Next 還有設定每個路由的 cache 時長,如果超過時間也會自動清除:
當路由切換時,若兩個頁面都有用到相同的 layout.tsx
UI,那這個 layout.tsx
UI 是不會重新渲染的,只會渲染差異的部分。
事實上,若兩頁共享了同一個 layout.tsx
UI,client 連這份 layout.tsx
的請求都不會發,因為它會直接使用 cache 的資料。
這有兩個好處:
layout.tsx
的負擔<Link>
與 useRouter
提供的都是軟導航(soft navigation),使用軟導航的好處就是可以保留頁面原本的狀態,舉凡 react 的狀態、dom focus 的狀態,這些都會保留下來。
而 browser 提供的 <a>
是硬導航(hard navigation),代表路由在切換的時候,整個頁面都會 reload,前一頁的所有狀態都會消失,包含 router cache,因為 cache 也是存在暫存記憶體當中。
這誰扛得住?
Next 的路由系統還會幫我們記憶 scroll 的位置。